package com.zhangzc.sc.scquery.Utils;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;


import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.*;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.metrics.ValueCount;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
 
/**
 * @author 
 * @date 2020-01-06 11:48
 * @description
 */
@Component
public class EsUtils {
 
    private static final long TIME_OUT = 60;
    private static final String KEY_WORD = ".keyword";
    private static final String COUNT = "count";
    private static final String INDEX_CREATION_DATE = "index.creation_date";
    private static final String KIBANA = ".kibana";
 
    @Autowired
    RestHighLevelClient highLevelClient;
    /**
     * 创建索引
     * @param index
     */
    public void createIndex(String index) {
        if (!existsIndex(index)) {
            try {
                CreateIndexRequest request = new CreateIndexRequest(index);
                mappings(request);
                settings(request);
                highLevelClient.indices().create(request, RequestOptions.DEFAULT);
            } catch (IOException io) {
                io.printStackTrace();
            }
        }
    }

    /**
     * 创建索引
     * @param index 索引
     * @param t 任何一个对象
     */
    public<T> void createIndex(String index,T t) {
        if (!existsIndex(index)) {
            try {
                CreateIndexRequest request = new CreateIndexRequest(index);
                mappingsByObject(request,t);
                settings(request);
                highLevelClient.indices().create(request, RequestOptions.DEFAULT);
            } catch (IOException io) {
                io.printStackTrace();
            }
        }
    }

    /**
     * 设置mapping,根据参数
     * @param request
     */
    private<T> void mappingsByObject(CreateIndexRequest request,T t) {

    }

    /**
     * 设置setting
     * @param request
     */
    public void settings(CreateIndexRequest request) {
        request.settings(Settings.builder()
                .put("index.number_of_shards", 5)     // 明确指定index前缀
                .put("index.number_of_replicas", 1)
        );
    }
 
    /**
     * 设置mapping
     * @param request
     */
    public void mappings(CreateIndexRequest request) {
        try {
            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject()
                    .startObject("properties")
                    .startObject("orderTime")
                    .field("type", "long")
                    .endObject()
                    .endObject()
                    .endObject();
            request.mapping(builder);
        } catch (IOException e) {
            throw new RuntimeException("Failed to build index mappings", e);
        }
    }
 
    /**
     * 判断ES索引是否存在
     * @param indexName
     * @return
     */
    public boolean existsIndex(String indexName) {
        try{
            GetIndexRequest request = new GetIndexRequest(indexName);
            return highLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        }catch (IOException e){
            e.printStackTrace();
        }
        return false;
    }
 
    /**
     * 查出ES所有索引，根据settings中的创建时间倒序排序
     * @return
     */
    public Set<String> getSortIndices(){
        GetSettingsRequest getSettingsRequest = new GetSettingsRequest();
        GetSettingsResponse getResponse = null;
        try {
            getResponse = highLevelClient.indices().getSettings(getSettingsRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ImmutableOpenMap<String, Settings> settings = getResponse.getIndexToSettings();
        Iterator<ObjectObjectCursor<String, Settings>> iterator = settings.iterator();
        Map<String,String> map = new HashMap<>();
        while (iterator.hasNext()) {
            ObjectObjectCursor<String, Settings> objectCursor = iterator.next();
            String key = objectCursor.key;
            Settings value = objectCursor.value;
            String creationDate = value.get(INDEX_CREATION_DATE);
            if (key.contains(KIBANA)) {
                continue;
            }
            map.put(key,creationDate);
        }
        return null;
    }
 
    /**
     * 查询ES所有索引
     * @return
     */
    public Set getIndices() {
        try {
            GetAliasesRequest request = new GetAliasesRequest();
            GetAliasesResponse getAliasesResponse =  highLevelClient.indices().getAlias(request,RequestOptions.DEFAULT);
            Map<String, Set<AliasMetadata>> map = getAliasesResponse.getAliases();
            Set<String> set = map.keySet();
            return set;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 查出最小值
     * @param name
     * @param field
     * @return
     */
    private AggregationBuilder min(String name,String field) {
        return AggregationBuilders.min(name).field(field);
    }
 
    /**
     * 查出最大值
     * @param name
     * @param field
     * @return
     */
    private AggregationBuilder max(String name,String field) {
        return AggregationBuilders.max(name).field(field);
    }
 
    /**
     * 统计条数
     * @param name
     * @param field
     * @return
     */
    private ValueCountAggregationBuilder count(String name,String field) {
        return AggregationBuilders.count(name).field(field);
 
    }
 
    /**
     * 多条件模糊查询
     * @param map (key为ES索引的字段，value为需要查询的值)
     * @return
     */
    public BoolQueryBuilder fuzzyQuery(Map<String,Object> map){
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        if (map != null && map.size() > 0) {
            for (Map.Entry<String,Object> entry : map.entrySet()) {
                Object value = entry.getValue();
                if (value instanceof String) {
                    if (StringUtils.isNotBlank(value.toString())) {
                        try{
                            // 当值转换不了数字时，try catch实现模糊查询
                            Long.parseLong(value.toString());
                            MatchPhraseQueryBuilder must = QueryBuilders.matchPhraseQuery(entry.getKey(), value);
                            queryBuilder.must(must);
                        } catch (Exception e) {
                            // 需要模糊查询时，value.toString()的值原本为：test 需要前后加上星号最后封装查询条件时变为：*test*
                            WildcardQueryBuilder must = QueryBuilders.wildcardQuery(entry.getKey()+KEY_WORD, value.toString());
                            queryBuilder.must(must);
                        }
                    }
                } else if (value instanceof Boolean) {
                    value = Boolean.parseBoolean(value.toString());
                    MatchPhraseQueryBuilder must = QueryBuilders.matchPhraseQuery(entry.getKey(), value);
                    queryBuilder.must(must);
                } else {
                    TermsQueryBuilder must = QueryBuilders.termsQuery(entry.getKey(), value);
                    queryBuilder.must(must);
                }
            }
        }
        return queryBuilder;
    }
 
    /**
     * 根据指定字段范围查询
     * @param name
     * @param start 开始值
     * @param end 结束值
     * @param format 格式化
     * @param queryBuilder
     */
    public void rangeQuery(String name,String start,String end,String format,
                           BoolQueryBuilder queryBuilder){
        if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(start)
                && StringUtils.isNotBlank(end)) {
            if (StringUtils.isBlank(start)) {
                start = "1990-01-01 00:00:00";
            }
            if (StringUtils.isBlank(end)) {
                end = "2099-01-01 00:00:00";
            }
            queryBuilder.must(QueryBuilders.rangeQuery(name+KEY_WORD).
                    from(start).to(end).format(format));
        }
    }
 
    /**
     * 根据条件获取索引总数
     * @param index
     * @param query
     * @return
     */
    public long getTotalHits(String index,String field,QueryBuilder query){
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(query);
        ValueCountAggregationBuilder aggregation =
                AggregationBuilders
                        .count(COUNT)
                        .field(field+KEY_WORD);
        sourceBuilder.aggregation(aggregation);
        SearchRequest request = new SearchRequest(index).source(sourceBuilder);
        try {
            SearchResponse response = highLevelClient.search(request, RequestOptions.DEFAULT);
            ValueCount agg = response.getAggregations().get(COUNT);
            return agg.getValue();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }
 
    /**
     * 根据条件查询指定索引数据
     * @param index ES索引
     * @param from 开始数
     * @param size 大小
     * @param orderName 排序字段名
     * @param sortOrder 排序方式
     * @param includes 返回字段
     * @param query 查询条件
     * @return
     */
    public List<Map<String, Object>> searchIndex(String index, int from, int size, String orderName, String sortOrder,
                            String[] includes, QueryBuilder query) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(query).from((from - 1) * size).size(size)
                .timeout(new TimeValue(TIME_OUT, TimeUnit.SECONDS));
        if (StringUtils.isNotBlank(orderName) && StringUtils.isNotBlank(sortOrder)) {
            sourceBuilder.sort(new FieldSortBuilder(orderName).order(SortOrder.fromString(sortOrder)));
        } else {
            sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
        }
        // 返回列和排除列,排除列可设置为null
        if (!CollectionUtils.isEmpty(includes)) {
            sourceBuilder.fetchSource(includes,null);
        }
        SearchRequest searchRequest = new SearchRequest(index).source(sourceBuilder);
        try{
            SearchResponse response = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            List<Map<String, Object>> data = new ArrayList<>();
            for(SearchHit hit : response.getHits().getHits()){
                Map<String, Object> map = hit.getSourceAsMap();
                data.add(map);
            }
            return data;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
 
}
